/*
 * Copyright (c) 2011, Nikolaus Moll
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of the gradecalculator.net nor the
 *   names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package net.gradecalculator.gui.qt.calculator.dialogs;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import net.gradecalculator.core.AdaptedRegulations;
import net.gradecalculator.core.enums.PassResult;
import net.gradecalculator.core.interfaces.ICourse;
import net.gradecalculator.core.interfaces.results.IResult;
import net.gradecalculator.core.interfaces.results.IResultType;
import net.gradecalculator.core.util.ResultTypeListUtil;
import net.gradecalculator.gui.qt.common.dialogs.AbstractDialog;
import net.gradecalculator.plugin.grade.interfaces.ICourseGrade;
import net.gradecalculator.plugin.grade.interfaces.IGradeFactory;
import net.gradecalculator.plugin.grade.interfaces.IGradeValidator;
import net.miblounge.miglayout.qt.MigLayout;

import com.trolltech.qt.gui.QCheckBox;
import com.trolltech.qt.gui.QGroupBox;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QLineEdit;
import com.trolltech.qt.gui.QWidget;

public abstract class AbstractResultDialog extends AbstractDialog
{
    private final IGradeFactory fab;
    private final QLineEdit[] leGrades;

    private QCheckBox cbUngradedResult;
    private QWidget groupEstimated;

    private final ICourse course;
    private final IResult examinationResult;

    private final AdaptedRegulations regulations;
    private final List<IResultType> resultTypes;
    private final IResultType resultTypePassed;

    public AbstractResultDialog(final QWidget parent, final AdaptedRegulations regulations, final ICourse course)
    {
        super(parent);
        setWindowTitle(tr("Result"));
        this.resultTypes = regulations.getStudent().getResultTypes();
        this.resultTypePassed = ResultTypeListUtil.getPassed(resultTypes);
        this.fab = regulations.getGradeFactory();
        this.course = course;
        this.regulations = regulations;
        this.examinationResult = null;
        // TODO
        // this.examinationResult = regulations.getStudent().getResult(course);

        this.leGrades = new QLineEdit[resultTypes.size()];

        setClientArea(createClientArea());

        setResults();

        revalidate();
    }

    protected abstract boolean hasGradedPerformance();

    protected abstract boolean hasUngradedPerformance();

    protected abstract boolean hasEstimatedGroup();

    protected abstract String getCourseName();

    protected void setEstimationEnabled(final boolean enabled)
    {
        groupEstimated.setEnabled(enabled);
    }

    @Override
    protected List<QWidget> createAdditonalWidgets(final QWidget parent)
    {
        return new Vector<QWidget>();
    }

    private QWidget createClientArea()
    {
        final QWidget result = new QWidget();
        final MigLayout layout = new MigLayout("", "[grow]", "");
        result.setLayout(layout);

        layout.addWidget(createActualGrade(result), "growx, wrap");
        if (hasEstimatedGroup()) {
            groupEstimated = createEstimatedGroup(result);
            layout.addWidget(groupEstimated, "growx, wrap");
        }

        for (final QWidget widget : createAdditonalWidgets(result)) {
            layout.addWidget(widget, "growx, wrap");
        }

        return result;
    }

    protected QGroupBox createActualGrade(final QWidget parent)
    {
        final QGroupBox result = new QGroupBox(tr("Actual grade for ") + getCourseName(), parent);
        final MigLayout layout = new MigLayout("", "[][grow, 100:100:200]", "[]5[]");
        result.setLayout(layout);

        if (hasGradedPerformance()) {
            final QLabel labelGrade = new QLabel(tr("Grade:"), result);
            // TODO
            // labelGrade.setToolTip(course.getGradedPerformance().toDetailedString());
            layout.addWidget(labelGrade, "sg lg");
            final QLineEdit leGrade = new QLineEdit(result);
            leGrade.textEdited.connect(this, "revalidate()");
            layout.addWidget(leGrade, "wrap");

            setGradeEdit(resultTypePassed, leGrade);
        }

        if (hasUngradedPerformance()) {
            final QLabel labelPractical = new QLabel(tr("Practical"));
            // TODO
            // labelPractical.setToolTip(course.getUngradedPerformance().toDetailedString());
            layout.addWidget(labelPractical, "sg lg");
            cbUngradedResult = new QCheckBox(tr("passed"), result);
            layout.addWidget(cbUngradedResult, "growx, wrap");

        }
        else {
            cbUngradedResult = null;
        }

        return result;
    }

    protected QGroupBox createEstimatedGroup(final QWidget parent)
    {
        final QGroupBox result = new QGroupBox(tr("Estamted Results for ") + getCourseName(), parent);
        final MigLayout layout = new MigLayout("", "[][grow, 100:100:200]", "[]5[]");
        result.setLayout(layout);

        for (final IResultType type : resultTypes) {
            if (type == resultTypePassed) {
                continue;
            }

            layout.addWidget(new QLabel(type.getName(), result), "sg lg");
            final QLineEdit lineEdit = new QLineEdit(result);
            lineEdit.textEdited.connect(this, "revalidate()");
            layout.addWidget(lineEdit, "wrap");

            setGradeEdit(type, lineEdit);
        }

        return result;
    }

    protected IGradeFactory getFab()
    {
        return fab;
    }

    protected void setGradeEdit(final IResultType type, final QLineEdit lineEdit)
    {
        setGradeEdit(type.ordinal(), lineEdit);
    }

    protected void setGradeEdit(final int index, final QLineEdit lineEdit)
    {
        leGrades[index] = lineEdit;
    }

    protected QLineEdit getGradeEdit(final IResultType type)
    {
        return getGradeEdit(type.ordinal());
    }

    protected QLineEdit getGradeEdit(final int index)
    {
        return leGrades[index];
    }

    @Override
    protected boolean validate()
    {
        if (!hasGradedPerformance()) {
            return super.validate();
        }

        final boolean isEstimated = leGrades[resultTypePassed.ordinal()].text().trim().equals("");
        setEstimationEnabled(isEstimated);

        final IGradeValidator validator = fab.getValidator();

        if (isEstimated) {
            for (final IResultType type : resultTypes) {
                if (type == resultTypePassed) {
                    continue;
                }

                final QLineEdit le = leGrades[type.ordinal()];
                if (!validator.isValid(le.text())) {
                    return false;
                }
            }
        }
        else {
            if (!validator.isValid(leGrades[resultTypePassed.ordinal()].text())) {
                return false;
            }
        }

        return super.validate();
    }

    public Map<IResultType, String> getGrades()
    {
        final Map<IResultType, String> result = new HashMap<IResultType, String>();

        final boolean isEstimated = leGrades[resultTypePassed.ordinal()].text().trim().equals("");

        if (isEstimated) {
            for (final IResultType type : resultTypes) {
                if (type == resultTypePassed) {
                    continue;
                }

                final QLineEdit le = leGrades[type.ordinal()];
                final ICourseGrade grade = fab.createCourseGrade(le.text(), 1);
                result.put(type, grade.getGrade());
            }
        }
        else {
            final ICourseGrade grade = fab.createCourseGrade(leGrades[resultTypePassed.ordinal()].text(), 1);
            result.put(resultTypePassed, grade.getGrade());
        }

        // if (leGrade != null) {
        // if (getGradedResult() == Result.NONE) {
        // return "";
        // }
        // return leGrade.text();
        // }

        return result;
    }

    protected IResult getResult()
    {
        return examinationResult;
    }

    protected void setResults()
    {
        if (hasUngradedPerformance()) {
            cbUngradedResult.setChecked(examinationResult.getUngradedPassed() == PassResult.PASSED);
        }

        // for (final ExaminationResultType type :
        // ExaminationResultType.values()) {
        // final String gradeText = examinationResult.getGrade(type);
        //
        // final QLineEdit lineEdit = getGradeEdit(type);
        // if (lineEdit == null) {
        // continue;
        // }
        // lineEdit.setText(gradeText);
        // if (type == resultTypePassed) {
        // lineEdit.setFocus();
        // lineEdit.setSelection(0, gradeText.length());
        // }
        // }
    }

    public boolean getUngradedPassed()
    {
        if (cbUngradedResult != null) {
            return cbUngradedResult.isChecked();
        }

        return false;
    }

    protected ICourse getCourse()
    {
        return course;
    }

    protected void setGradedPerformanceEnabled(final boolean enabled)
    {
        getGradeEdit(resultTypePassed).setEnabled(enabled);
        if (!enabled) {
            setEstimationEnabled(false);
        }
        else {
            final boolean isEstimated = leGrades[resultTypePassed.ordinal()].text().trim().equals("");
            setEstimationEnabled(isEstimated);
        }
    }

    protected void setUngradedPerformanceEnabled(final boolean enabled)
    {
        cbUngradedResult.setEnabled(enabled);
    }

    @Override
    public void apply()
    {
        super.apply();
        // TOOD
        // regulations.getStudent().addResult(getResult());
    }

    public AdaptedRegulations getRegulations()
    {
        return regulations;
    }
}
